using System;
using Sirenix.OdinInspector;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

namespace Utils.MaterialProperty.UGUI
{
    [ExecuteAlways]
    [DisallowMultipleComponent]
    [AddComponentMenu("Material Property/UGUI/Handle")]
    public class MaterialSetterHandleUni : UIBehaviour, IMaterialModifier
    {
        private static readonly int Stencil = Shader.PropertyToID("_Stencil");
        private static readonly int StencilOp = Shader.PropertyToID("_StencilOp");
        private static readonly int StencilComp = Shader.PropertyToID("_StencilComp");
        private static readonly int StencilWriteMask = Shader.PropertyToID("_StencilWriteMask");
        private static readonly int StencilReadMask = Shader.PropertyToID("_StencilReadMask");
        private static readonly int ColorMask = Shader.PropertyToID("_ColorMask");

        [SerializeField] [OnValueChanged("OnGraphicChanged")]
        private Graphic uiGraphic;

        [SerializeField] [OnValueChanged("SetMaterial")]
        private Shader shader;

        [SerializeField]
        private MaterialPropertyUniData[] materialPropertyUniDataArray;

        [SerializeField] [OnValueChanged("OnGraphicChanged")]
        private MaterialPropertyUni[] materialPropertyUniArray;

        private Material mat;

        protected override void Awake()
        {
            base.Awake();
            if (uiGraphic == null)
                uiGraphic = GetComponent<Graphic>();
        }

        protected override void OnEnable()
        {
            base.OnEnable();
            if (uiGraphic == null)
                uiGraphic = GetComponent<Graphic>();

#if UNITY_EDITOR
            ReadAllPropertySetter(); // 确保数组引用修复
            #endif
            SetMaterial();
            OnGraphicChanged();
            SetContent();
            OnRectTransformDimensionsChange();
        }

        private void SetMaterial()
        {
            if (shader == null || uiGraphic == null)
                return;

            if (mat == null || mat.shader != shader)
            {
                mat = new Material(shader);
                uiGraphic.material = mat;
            }
        }

        private void OnGraphicChanged()
        {
            if (materialPropertyUniArray != null)
            {
                if (materialPropertyUniDataArray == null ||
                    materialPropertyUniDataArray.Length != materialPropertyUniArray.Length)
                {
                    materialPropertyUniDataArray = new MaterialPropertyUniData[materialPropertyUniArray.Length];
                }

                for (var index = 0; index < materialPropertyUniArray.Length; index++)
                {
                    var setter = materialPropertyUniArray[index];
                    setter.SetGraphic(uiGraphic);
                    materialPropertyUniDataArray[index] ??= new MaterialPropertyUniData();
                    setter.ReadData(materialPropertyUniDataArray[index]);
                }
            }
        }

        private void OnDataValueChanged()
        {
            if (materialPropertyUniArray != null && materialPropertyUniDataArray != null &&
                materialPropertyUniDataArray.Length == materialPropertyUniArray.Length)
            {
                for (var index = 0; index < materialPropertyUniArray.Length; index++)
                {
                    var setter = materialPropertyUniArray[index];
                    setter.ApplyData(materialPropertyUniDataArray[index]);
                }
            }
        }

        public Material GetModifiedMaterial(Material baseMaterial)
        {
            if (shader == null || uiGraphic == null)
                return baseMaterial;

            // 每次都基于 baseMaterial 新建，确保 stencil 参数正确
            if (mat == null || mat.shader != shader)
            {
                mat = new Material(baseMaterial)
                {
                    shader = shader
                };
            }
            else
            {
                SyncStencilProperties(baseMaterial, mat);
            }

            return mat;
        }

        private void SyncStencilProperties(Material src, Material dst)
        {
            if (src.HasProperty(Stencil)) dst.SetInt(Stencil, src.GetInt(Stencil));
            if (src.HasProperty(StencilOp)) dst.SetInt(StencilOp, src.GetInt(StencilOp));
            if (src.HasProperty(StencilComp)) dst.SetInt(StencilComp, src.GetInt(StencilComp));
            if (src.HasProperty(StencilWriteMask)) dst.SetInt(StencilWriteMask, src.GetInt(StencilWriteMask));
            if (src.HasProperty(StencilReadMask)) dst.SetInt(StencilReadMask, src.GetInt(StencilReadMask));
            if (src.HasProperty(ColorMask)) dst.SetInt(ColorMask, src.GetInt(ColorMask));
        }

        public MaterialPropertyUni GetPropertySetter(int propertyId)
        {
            foreach (var setter in materialPropertyUniArray)
            {
                if (setter.PropertyId == propertyId)
                    return setter;
            }
            return null;
        }

#if UNITY_EDITOR
        protected override void Reset()
        {
            base.Reset();
            uiGraphic = GetComponent<Graphic>();
        }

        protected override void OnValidate()
        {
            base.OnValidate();
            OnDataValueChanged();
            SetContent();
        }

        [Button]
        private void ReadAllPropertySetter()
        {
            materialPropertyUniArray = gameObject.GetComponents<MaterialPropertyUni>();
            OnGraphicChanged();
        }

        [Button]
        private void AddSetter()
        {
            var com = gameObject.AddComponent<MaterialPropertyUni>();
            ReadAllPropertySetter();
        }
#endif

        protected override void OnCanvasHierarchyChanged()
        {
            base.OnCanvasHierarchyChanged();
            OnGraphicChanged();
            SetContent();
        }

        protected override void OnRectTransformDimensionsChange()
        {
            base.OnRectTransformDimensionsChange();
            if (materialPropertyUniArray != null)
            {
                foreach (var setter in materialPropertyUniArray)
                    setter.OnRectTransformChange();
            }
            uiGraphic?.SetMaterialDirty();
        }

        void SetContent()
        {
            if (mat == null || materialPropertyUniArray == null || materialPropertyUniArray.Length == 0)
                return;

            foreach (var setter in materialPropertyUniArray)
                setter.SetMaterialProperty();

            uiGraphic?.SetMaterialDirty();
        }

        public void ApplyChange()
        {
            SetContent();
        }

        public void ApplyChange(MaterialPropertyUni setter)
        {
            setter.SetMaterialProperty();
            uiGraphic?.SetMaterialDirty();
        }
    }
}